#include "categoriesmodel.h"

CategoriesModel::CategoriesModel(QSqlDatabase database, QObject *parent) :
    QAbstractItemModel(parent)
{
    // On défini la base
    if(!database.isValid()){
        db = QSqlDatabase::database();
    } else {
        db = database;
    }

    racine = new CategorieItem(0);
    raffraichir(0);

}

CategoriesModel::~CategoriesModel()
{
    delete racine;
}

QVariant CategoriesModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid()){
        return QVariant();
    }

    if (role != Qt::DisplayRole){
        return QVariant();
    }

    CategorieItem *item = static_cast<CategorieItem*>(index.internalPointer());

    return item->data();
}

QVariant CategoriesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    // Si l'orientation le role et la section correspondent on renvoi Catégories sinon rien
    if(section == 0 && orientation == Qt::Horizontal && role == Qt::DisplayRole){
        return racine->data();
    }
    return QVariant();
}

QModelIndex CategoriesModel::index(int row, int column, const QModelIndex &parent) const
{
    // On cherche à renvoyer l'index enfant situé à la ligne row de l'index parent

    // Si le modèle n'a pas d'index pour ces coordonnées (row ou column trop grand pour le parent)
    if (!hasIndex(row, column, parent)){
            return QModelIndex();
    }

    // On créer le pointeur sur l'item parent
    CategorieItem *parentItem;

    // On récupère l'item parent (racine par defaut)
    if (!parent.isValid()){
        parentItem = racine;
    } else {
        parentItem = static_cast<CategorieItem*>(parent.internalPointer());
    }

    // On récupère l'item enfant depuis le parent
    CategorieItem *childItem = parentItem->enfant(row);

    // S'il existe, on crée un index pour y accéder
    if (childItem){
        return createIndex(row, column, childItem);
    } else {
        return QModelIndex();
    }
}

QModelIndex CategoriesModel::parent(const QModelIndex &index) const
{
    // On veut renvoyer l'index parent du notre

    // Si l'index n'est pas valide, il n'a pas de parent
    if (!index.isValid()){
        return QModelIndex();
    }

    // On récupère l'item associé puis le parent de cet item
    CategorieItem *childItem = static_cast<CategorieItem*>(index.internalPointer());
    CategorieItem *parentItem = childItem->getParent();

    // Si le parent est racine, l'index n'est pas valide (racine n'est pas un item valide)
    if (parentItem == racine){
        return QModelIndex();
    }

    // On crée l'index correspondant au parent
    return createIndex(parentItem->row(), 0, parentItem);
}

int CategoriesModel::rowCount(const QModelIndex &parent) const
{
    // On souhaite sélectionner le nombre de ligne d'un index parent, autrement dit le nombre d'enfants de l'item associé

    // Création d'un pointeur sur l'item parent
    CategorieItem *parentItem;

    // Si l'index est invalide on récupère l'item racine, sinon on récupère l'item dans l'index
    if (!parent.isValid()){
        parentItem = racine;
    } else {
        parentItem = static_cast<CategorieItem*>(parent.internalPointer());
    }

    // On retourne le nombre de ligne de l'item
    return parentItem->numRow();
}

int CategoriesModel::columnCount(const QModelIndex &parent) const
{
    // Il n'y a qu'une colonne dans ce model c'est le nom

    return 1;
}

Qt::ItemFlags CategoriesModel::flags(const QModelIndex &index) const
{
    // Si l'index n'est pas valide on ne renvoie pas de flag
    if (!index.isValid()){
        return 0;
    }

    // Sinon on retourne les flags de la classe mère
    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}

void CategoriesModel::raffraichir(int root)
{
    //On demarre la procédure de Reset
    this->beginResetModel();

    // On efface l'arbre
    delete racine;

    // On le reconstruit sur la racine demandée
    racine = new CategorieItem(0);
    racine->construireArbre(db,root);

    //On termine la procédure
    this->endResetModel();
}

bool CategoriesModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    // Le but est de changer le nom d'une catégorie

    if(role != Qt::EditRole){
        return false;
    }
    // On retrouve le pointeur de la catégorie
        // Si l'index est invalide on retourne faux
    if(!index.isValid()){
        return false;
    }
        //On récupère le pointeur
    CategorieItem *item = static_cast<CategorieItem*>(index.internalPointer());

    // On envoie une requète à la base

    QSqlQuery q(db);
    q.prepare("UPDATE categories SET nom = :n WHERE id = :i");
    q.bindValue(":i",item->getId());
    q.bindValue(":n",value.toString());

    if(q.exec()){
        item->setData(value);
        emit dataChanged(index,index);
        return true;
    }
    return false ;

}

bool CategoriesModel::insertRows(int row, int count, const QModelIndex &parent)
{
    // On veut insérer un nouvel item vide
    // On crée l'item parent
    CategorieItem *parentItem ;

    // On récupère l'item parent
    if(parent.isValid()){
        parentItem = static_cast<CategorieItem*>(parent.internalPointer());
    } else {
        parentItem = racine;
    }

    // On créé une liste pour stocker les nouveaux ids
    QList<int> ids;


    db.transaction();
    QSqlQuery q(db);

    // On décale tout les suivants
    q.prepare("UPDATE categories SET ordre = ordre + :c WHERE ordre > :r AND parent = :p");
    q.bindValue(":c",count);
    q.bindValue(":r",row - 1);
    q.bindValue(":p",parentItem->getId());
    q.exec();

    QSqlQuery q1(db);

    // On insert les lignes
    q1.prepare("INSERT INTO categories (parent,nom,ordre) VALUES (:p,'',:o)");
    q1.bindValue(":p",parentItem->getId());
    for(int i = row; i< row + count; i++){
        q1.bindValue(":o",i);
        q1.exec();
        ids << q1.lastInsertId().toInt();
    }

    if(db.commit()){

        // On démarre la procédure
        beginInsertRows(parent,row,row + count -1);

        for(int i = row; i< row + count; i++){
            parentItem->insertEnfant(i,ids.at(i));
        }

        endInsertRows();
        return true;
    }

    return false;
}

bool CategoriesModel::ordrePlus(const QModelIndex &index)
{
    // On vérifie la validité de l'index
    if(!index.isValid()){
        return false;
    }

    // On récupère l'item
    CategorieItem * item = static_cast<CategorieItem*>(index.internalPointer());

    if(item->row() < item->getParent()->numRow() - 1){
        // Si on peut le monter
        int idNext = item->getParent()->enfant(index.row()+1)->getId();


        db.transaction();
        QSqlQuery q(db);
        q.prepare("UPDATE categories SET ordre = :o WHERE id = :i");

        // On modifie l'item suivant
        q.bindValue(":o",index.row());
        q.bindValue(":i",idNext);
        q.exec();

        // On modifie l'item
        q.bindValue(":o",index.row() + 1);
        q.bindValue(":i",item->getId());
        q.exec();

        if(!db.commit()){
            return false;
        }

        beginMoveRows(index.parent(), index.row(), index.row(), index.parent(), index.row() + 2);

        item->getParent()->swap(index.row(),index.row()+1);

        endMoveRows();

        return true;


    } else {
        return false;
    }


}

bool CategoriesModel::ordreMoins(const QModelIndex &index)
{
    // On vérifie la validité de l'index
    if(!index.isValid()){
        return false;
    }

    // On récupère l'item
    CategorieItem * item = static_cast<CategorieItem*>(index.internalPointer());

    if(item->row() > 0){
        // Si on peut le descendre
        int idPrev = item->getParent()->enfant(index.row()-1)->getId();


        db.transaction();
        QSqlQuery q(db);
        q.prepare("UPDATE categories SET ordre = :o WHERE id = :i");

        // On modifie l'item suivant
        q.bindValue(":o",index.row());
        q.bindValue(":i",idPrev);
        q.exec();

        // On modifie l'item
        q.bindValue(":o",index.row() - 1);
        q.bindValue(":i",item->getId());
        q.exec();

        if(!db.commit()){
            return false;
        }

        beginMoveRows(index.parent(), index.row(), index.row(), index.parent(), index.row() - 1);

        item->getParent()->swap(index.row(),index.row()-1);

        endMoveRows();

        return true;


    } else {
        return false;
    }
}

bool CategoriesModel::moveDown(const QModelIndex &index)
{
    // On vérifie que l'index possède un élément précédent
    if(!index.isValid() || index.row()==0){
        return false;
    }

    //On récupère l'item
    CategorieItem * item = static_cast<CategorieItem*>(index.internalPointer());

    //On récupère l'id du précédent
    CategorieItem * itemPrev = item->getParent()->enfant(index.row()-1);

    //On le passe en dernier
    while(item->row() < item->getParent()->numRow() - 1){
        ordrePlus(createIndex(item->row(),0,item));
    }



    // On récupère le dernier pour vérifier
    CategorieItem *item2 = item->getParent()->retirerDernier();

    if(item != item2){
        // On le réinsère si erreur
        item->getParent()->insertEnfant(item2->row(),item2);
        return false;
    }

    // On traite au niveau Sql
    db.transaction();

    QSqlQuery q(db);
    q.prepare("UPDATE categories SET ordre = :o , parent = :p WHERE id = :i");
    q.bindValue(":i",item->getId());
    q.bindValue(":p",itemPrev->getId());
    q.bindValue(":o",itemPrev->numRow());
    q.exec();

    if(!db.commit()){
        // On le réinsère si erreur
        item->getParent()->insertEnfant(item->row(),item);
        return false;
    }

    // On procède au décalage
    beginMoveRows(index.parent(),index.row(),index.row(),this->index(itemPrev->row(),0,index.parent()),itemPrev->numRow());

    itemPrev->insertEnfant(itemPrev->numRow(),item);

    endMoveRows();

    return true;

}

bool CategoriesModel::moveUp(const QModelIndex &index)
{

    // On vérifie que l'index est valide
    if(!index.isValid()){
        return false;
    }
    // On vérifie que l'index du parent est valide
    if(!index.parent().isValid()){
        return false;
    }

    //On récupère l'item
    CategorieItem * item = static_cast<CategorieItem*>(index.internalPointer());

    //On récupère l'item parent
    CategorieItem * itemParent = item->getParent();

    //On le passe en dernier
    while(item->row() < itemParent->numRow() - 1){
        ordrePlus(createIndex(item->row(),0,item));
    }



    // On traite au niveau Sql
    db.transaction();

    QSqlQuery q(db);
    q.prepare("UPDATE categories SET ordre = :o , parent = :p WHERE id = :i");
    q.bindValue(":i",item->getId());
    q.bindValue(":p",itemParent->getParent()->getId());
    q.bindValue(":o",itemParent->getParent()->numRow());
    q.exec();

    if(!db.commit()){
        return false;
    }

    // On procède au décalage
    beginMoveRows(index.parent(),index.row(),index.row(),index.parent().parent(),itemParent->getParent()->numRow());

    itemParent->retirerDernier();
    itemParent->getParent()->insertEnfant(itemParent->getParent()->numRow(),item);

    endMoveRows();

    return true;

}



